package cn.lau.demo.auth;

import cn.lau.demo.util.HttpUtil;
import cn.lau.demo.util.JWTUtil;
import com.alibaba.fastjson.JSON;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author War horse imwarhorse@aliyun.com
 * Date: 2020/12/23
 * Description: JWT相关过滤器
 * 执行顺序：preHandle() -> isAccessAllowed() -> onAccessDenied()
 */

@Component
public class JWTFilter extends BasicHttpAuthenticationFilter {

	private final static Logger LOGGER = LoggerFactory.getLogger(JWTFilter.class);

	@Override
	protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest servletRequest = (HttpServletRequest) request;
		HttpServletResponse servletResponse = (HttpServletResponse) response;
		servletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, servletRequest.getHeader(HttpHeaders.ORIGIN));
		servletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,OPTIONS,PUT,DELETE");
		servletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, servletRequest.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
		// 当发生跨域时，客户端会先发送一个OPTION请求以确保请求安全性，直接给OPTION请求返回正常状态
		if (servletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
			servletResponse.setStatus(HttpStatus.OK.value());
			// OPTION请求不需要执行任何实际业务逻辑，可直接中断并返回
			return false;
		}
		return super.preHandle(servletRequest, servletResponse);
	}

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
		Subject subject = SecurityUtils.getSubject();
		return null != subject && subject.isAuthenticated();    // 如果已认证则直接允许访问，否则(未认证)拒绝访问，走onAccessDenied()
	}

	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		System.out.println("JWTFilter.onAccessDenied");
		String token = HttpUtil.getHeaderVal(request, JWTUtil.TOKEN_HEADER);
		if (null == token || token.length() == 0) {
			// 不带token访问
			responseTokenError(response, "缺少令牌");
			return false;
		}
		JWTToken jwtToken = new JWTToken(token);
		try {
			SecurityUtils.getSubject().login(jwtToken);
		} catch (AuthenticationException e) {
			LOGGER.info("通过Token认证异常，信息：" + e.getMessage());
			responseTokenError(response, e.getMessage());
			return false;
		}
		return true;
	}

	/**
	 * 无需转发，直接返回Response信息 Token认证错误
	 */
	private void responseTokenError(ServletResponse response, String msg) {
		HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
		httpServletResponse.setStatus(HttpStatus.OK.value());
		httpServletResponse.setCharacterEncoding("UTF-8");
		httpServletResponse.setContentType("application/json; charset=utf-8");
		try (PrintWriter out = httpServletResponse.getWriter()) {
			Map<String, Object> result = new HashMap<>();
			result.put("code", 1);
			result.put("msg", msg);
			result.put("data", null);
			String data = JSON.toJSONString(result);
			out.append(data);
		} catch (IOException e) {
			e.printStackTrace();
			LOGGER.error(e.getMessage());
		}
	}

}
